import os

import jwt
import hashlib
from datetime import datetime, timedelta
from sqlalchemy import desc

from app.db import SessionLocal, teacher_management_sessionLocal
from app.db.model import get_class_by_tablename

from app.envConfig import ENV_CONFIG

from functools import wraps
from flask import request, g

from app.utils.exceptions import TokenExpiredError, InvalidTokenError, MissingAuthorizationHeaderError, \
    PermissionDeniedError


# 改为在需要时导入，例如在某个函数内：
def get_encrypt_session():
    from app.utils.encryption import encrypt_session
    return encrypt_session


def get_decrypt_session():
    from app.utils.encryption import decrypt_session
    return decrypt_session


RefreshToken = get_class_by_tablename("refresh_tokens")
ConsumableApplyRefreshToken = get_class_by_tablename("consumable_apply_refresh_tokens")
TOKEN_TABLE = {
    "bigdata": [SessionLocal, RefreshToken],
    "consumable": [teacher_management_sessionLocal, ConsumableApplyRefreshToken]
}


# ====================== 工具函数 ======================
def generate_jti():
    """生成32位十六进制唯一标识"""
    return os.urandom(16).hex()


def hash_token(token: str) -> str:
    """使用SHA-256哈希存储token（实际生产应使用bcrypt）"""
    return hashlib.sha256(token.encode()).hexdigest()


def create_token(payload: dict, expires_delta: timedelta) -> str:
    """
    创建带安全声明的JWT
    :param payload: 原始声明数据
    :param expires_delta: 有效期时长
    :return: 签名后的token字符串
    """
    payload = payload.copy()
    if 'exp' not in payload:
        expire = datetime.utcnow() + expires_delta
        payload.update({'exp': expire})
    payload.update({
        'iat': datetime.utcnow(),
        'iss': 'lxx'  # 签发者标识
    })
    return jwt.encode(
        payload,
        ENV_CONFIG.JWT_SECRET_KEY,
        algorithm=ENV_CONFIG.JWT_ALGORITHM
    )


# ====================== 核心逻辑 ======================
def generate_tokens(user_id: str, role: str, client_fp: str, website_name: str = "bigdata") -> dict:
    """
    生成双token对
    :param client_fp: 客户端指纹（由前端生成）
    """
    # 统一时间基准点
    now = datetime.utcnow()

    # 撤销所有旧token
    token_table = TOKEN_TABLE.get(website_name)
    table = token_table[1]
    with token_table[0]() as session:
        session.query(table).filter(
            table.user_id == user_id,
            table.revoked == False
        ).update({table.revoked: True})
        session.commit()

    # 生成access token
    access_token = create_token(
        payload={
            'sub': user_id,
            'role': role,
            'type': 'access',
        },
        expires_delta=timedelta(minutes=ENV_CONFIG.ACCESS_EXPIRE_MINUTES)
    )

    # 生成refresh token
    refresh_token = create_token(
        payload={
            'sub': user_id,
            'jti': generate_jti(),
            'type': 'refresh',
            'fp': hash_token(client_fp),
        },
        expires_delta=timedelta(days=ENV_CONFIG.REFRESH_EXPIRE_DAYS)
    )

    print(refresh_token)

    # 持久化存储refresh token
    with token_table[0]() as session:
        decoded = jwt.decode(refresh_token, ENV_CONFIG.JWT_SECRET_KEY, algorithms=[ENV_CONFIG.JWT_ALGORITHM])
        session.add(table(
            jti=decoded['jti'],
            user_id=user_id,
            token_hash=hash_token(refresh_token),
            expires=datetime.utcfromtimestamp(decoded['exp']),
            client_fp=decoded['fp']
        ))
        session.commit()

        encrypted_refresh_token = get_encrypt_session()(refresh_token)

    return {
        'access_token': access_token,
        'refresh_token': encrypted_refresh_token
    }


def revoke_token(user_id: str, website_name: str = "bigdata"):
    """撤销指定refresh token"""
    token_table = TOKEN_TABLE.get(website_name)
    table = token_table[1]
    with token_table[0]() as session:
        token = (
            session.query(table)
            .filter_by(user_id=user_id, revoked=0)  # 过滤 user_id 且未被撤销
            .order_by(desc(table.created_at))  # 按时间倒序排列
            .first()  # 取最新一条
        )
        if token:
            token.revoked = True
            session.commit()


def login_required(f):
    @wraps(f)  # 保留被装饰函数的元数据
    def decorated_function(*args, **kwargs):
        # 检查access token
        auth_header = request.headers.get('Authorization')
        if not auth_header or not auth_header.startswith('Bearer '):
            raise MissingAuthorizationHeaderError({"o-facingMsg": "授权头失效"})
        token = auth_header.split(' ')[1].strip()
        if not token:
            raise MissingAuthorizationHeaderError({"o-facingMsg": "授权头失效"})

        # 验证Token有效性
        user_info = validate_access_token(token)
        if not user_info:
            raise MissingAuthorizationHeaderError({"o-facingMsg": "授权头失效"})
        g.user_role = user_info.get("user_role")
        g.user_id = user_info.get("user_id")
        print("test3".center(60, "="))
        print(user_info)
        print("test3".center(60, "="))
        return f(*args, **kwargs)

    return decorated_function


def teacher_required(f):
    @wraps(f)  # 保留被装饰函数的元数据
    def decorated_function(*args, **kwargs):
        request_path = request.path
        # 检查access token
        auth_header = request.headers.get('Authorization')
        if not auth_header or not auth_header.startswith('Bearer '):
            raise MissingAuthorizationHeaderError({"o-facingMsg": "授权头失效"})
        token = auth_header.split(' ')[1].strip()
        if not token:
            raise MissingAuthorizationHeaderError({"o-facingMsg": "授权头失效"})

        # 验证Token有效性
        user_info = validate_access_token(token)
        if not user_info:
            raise MissingAuthorizationHeaderError({"o-facingMsg": "授权头失效"})
        g.user_role = user_info.get("user_role")
        g.user_id = user_info.get("user_id")
        if g.user_role not in ["teacher", "group_leader", "department_head", "admin", "dev_operator"]:
            raise PermissionDeniedError(request_path, user_id=user_info.get("user_id"))
        print("test3".center(60, "="))
        print(user_info)
        print("test3".center(60, "="))
        return f(*args, **kwargs)

    return decorated_function


def validate_access_token(token):
    try:
        # 解码并验证JWT
        payload = jwt.decode(token, ENV_CONFIG.JWT_SECRET_KEY, algorithms=[ENV_CONFIG.JWT_ALGORITHM])
        print("test".center(60, "="))
        print(payload)
        print("test".center(60, "="))

        if payload.get('type') != 'access':
            raise InvalidTokenError({"o-facingMsg": "无效的令牌类型"})

        # 检查Token是否过期
        expire_timestamp = payload.get('exp')
        if not expire_timestamp or datetime.utcnow().timestamp() > expire_timestamp:
            raise InvalidTokenError({"o-facingMsg": "令牌失效"})

        print("test2".center(60, "="))
        print(expire_timestamp)
        print("test2".center(60, "="))
        # 返回用户信息（示例，实际应根据业务逻辑调整）
        return {
            'user_id': payload.get('sub'),
            'user_role': payload.get('role')
        }

    except jwt.ExpiredSignatureError:
        raise InvalidTokenError({"o-facingMsg": "无效令牌"})
    except jwt.InvalidTokenError:
        raise InvalidTokenError({"o-facingMsg": "无效令牌"})
    except Exception as e:
        raise InvalidTokenError({"o-facingMsg": "令牌验证失败"}) from e


def query_refresh_token(encrypted_token, client_fp, website_name: str = "bigdata"):
    try:
        # 解密token
        decrypt = get_decrypt_session()  # 这是对称加解密方法
        raw_token = decrypt(encrypted_token)

        # 解码验证
        payload = jwt.decode(
            raw_token,
            ENV_CONFIG.JWT_SECRET_KEY,
            algorithms=[ENV_CONFIG.JWT_ALGORITHM]
        )

        # 校验类型
        if payload.get('type') != 'refresh':
            raise InvalidTokenError({"o-facingMsg": "无效的令牌类型"})

        # 获取关键信息
        jti = payload.get('jti')
        user_id = payload.get('sub')
        token_fp = payload.get('fp')
        print("test".center(60, "="))
        print(token_fp)
        print("test".center(60, "="))
        print("test1".center(60, "="))
        print(client_fp)
        print("test1".center(60, "="))

        # 验证客户端指纹
        if hash_token(client_fp) != token_fp:
            print("hash".center(60, "="))
            print(hash_token(client_fp))
            print("hash".center(60, "="))
            raise InvalidTokenError({"o-facingMsg": "客户端不匹配"})

        # 数据库查询
        token_table = TOKEN_TABLE.get(website_name)
        table = token_table[1]
        with token_table[0]() as session:
            token_record = (
                session.query(table)
                .filter_by(jti=jti, user_id=user_id, revoked=False)
                .first()
            )

        if not token_record:
            raise InvalidTokenError({"o-facingMsg": "令牌不存在"})

        # 验证哈希值
        if token_record.token_hash != hash_token(raw_token):
            raise InvalidTokenError({"o-facingMsg": "令牌无效"})

        # 验证有效期
        print("time".center(80, "="))
        print(datetime.utcnow())
        print(token_record.expires)
        print("time".center(80, "="))
        if datetime.utcnow() > token_record.expires:
            raise TokenExpiredError(token_record.expires)

        return user_id

    except jwt.ExpiredSignatureError as e:
        raise InvalidTokenError({"o-facingMsg": "令牌无效"}) from e
    except (jwt.InvalidTokenError, AttributeError) as e:
        raise InvalidTokenError({"o-facingMsg": "无效令牌"}) from e
